#include <sys/socket.h>
#include <netinet/in.h>

#include <stdio.h>
#include <getopt.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <libubox/ustream.h>
#include <libubox/uloop.h>
#include <libubox/usock.h>

static const char *port = "10000";
static struct ustream_fd sfd;
//enum client_state state;

enum cmd_id {
	CMD_REQUEST_DOORID = 'A',
	CMD_RESPONSE_DOORID = 'B',
	CMD_REQUEST_OPENDOOR = 'C',
	CMD_RESPONSE_OPENDOOR = 'D',
	CMD_RANDOMSEED = 'E',
};

static int request_open_door(struct ustream *s)
{
	ustream_write(s, "C\0", 2, false);
}

static void handle_response(struct ustream *s, char *data, int len)
{
	int length;
	switch (data[0]) {
	case CMD_RESPONSE_DOORID:
		length = data[1];
		fprintf(stderr, "doorid: %s\n", &data[2]);

		request_open_door(s);
		// comment to emnulate timeout
		//ustream_write(s, "Bopendoorrequest\n", strlen("Bopendoorrequest\n"), false);
		break;
	case CMD_RESPONSE_OPENDOOR:
		if (data[2] == 0)
			fprintf(stderr, "opendoor failed\n");
		else if (data[2] == 1)
			fprintf(stderr, "opendoor successfully\n");
		else		
			fprintf(stderr, "internal error\n");

		uloop_end();
		
		break;
	}

}

static void client_read_cb(struct ustream *s, int bytes)
{
	//struct ustream_buf *buf = s->r.head;
	char *data;
	int len;
	int i;

	do {
		data = ustream_get_read_buf(s, &len);
		if (!data)
			break;

		//for (i = 0; i<len; i++)
		//	printf("%c", data[i]);
		//printf("\n");

		handle_response(s, data, len);

		ustream_consume(s, len);
	} while(1);

	if (s->w.data_bytes > 256 && !ustream_read_blocked(s)) {
		fprintf(stderr, "Block read, bytes: %d\n", s->w.data_bytes);
		ustream_set_read_blocked(s, true);
	}
}

static void client_close()
{
	fprintf(stderr, "Connection closed\n");
	ustream_free(&sfd.stream);
	uloop_end();
}

static void client_notify_write(struct ustream *s, int bytes)
{
	fprintf(stderr, "Wrote %d bytes, pending: %d\n", bytes, s->w.data_bytes);

	if (s->w.data_bytes < 128 && ustream_read_blocked(s)) {
		fprintf(stderr, "Unblock read\n");
		ustream_set_read_blocked(s, false);
	}
}

static void client_notify_state(struct ustream *s)
{
	if (!s->eof)
		return;

	fprintf(stderr, "eof!, pending: %d\n", s->w.data_bytes);
	if (!s->w.data_bytes)
		return client_close();

}

static void init_request()
{
	ustream_write(&sfd.stream, "Arequest_door_id", strlen("Arequest_door_id"), false);
}

static int run_client(void)
{
	int fd;

	uloop_init();

	fd =  usock(USOCK_TCP | USOCK_IPV4ONLY | USOCK_NUMERIC, "127.0.0.1", port);
	if (fd < 0)
		return -1;

	sfd.stream.notify_read = client_read_cb;
	sfd.stream.notify_state = client_notify_state;
	sfd.stream.notify_write = client_notify_write;

	ustream_fd_init(&sfd, fd);	

	init_request();

	uloop_run();
	uloop_done();

	return 0;
}

static int usage(const char *name)
{
	fprintf(stderr, "Usage: %s -p <port>\n", name);
	return 1;
}

int main(int argc, char **argv)
{
	int ch;

	while ((ch = getopt(argc, argv, "p:")) != -1) {
		switch(ch) {
		case 'p':
			port = optarg;
			break;
		default:
			return usage(argv[0]);
		}
	}

	return run_client();
}
